# -*- coding: utf-8 -*-
# @Time    : 2024/7/1 10:22
# @Author  : yujiahao
# @File    : 05_numpy_fun.py
# @description:NumPy相关数组操作


"""
NumPy 中包含了一些处理数组的常用方法，大致可分为以下几类：

1. 数组变维操作
2. 数组转置操作
3. 修改数组维度操作
4. 连接与分割数组操作

下面分别对它们进行介绍。

数组变维操作：
函数名称    函数介绍
reshape     在不改变数组元素的条件下，修改数组的形状。
flat        返回一个迭代器，可以用 for 循环遍历其中的每一个元素。
flatten     以一维数组的形式返回一份数组的副本，对副本的操作不会影响到原数组。
ravel       返回一个连续的扁平数组（即展开的一维数组），与 flatten 不同，它返回的是数组视图（修改视图会影响原数组）。
"""
import numpy as np


# todo 1、数组变维操作
def numpy_variable_dimension():
    a = np.arange(9).reshape(3, 3)
    for row in a:
        print(row)

    # todo 1) numpy.ndarray.flat
    # numpy.ndarray.flat 返回一个数组迭代器
    for ele in a.flat:
        print(ele, end="，")

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # todo 2) numpy.ndarray.flatten()
    # numpy.ndarray.flatten 返回一份数组副本，对副本修改不会影响原始数组 ndarray.flatten(order='C')
    a = np.arange(8).reshape(2, 4)
    print(a)
    # 默认按行C风格展开的数组
    print(a.flatten())
    # 以F风格顺序展开的数组
    print(a.flatten(order='F'))

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # todo 3) numpy.ravel()

    # numpy.ravel() 将多维数组中的元素以一维数组的形式展开，该方法返回数组的视图（view），如果修改，则会影响原始数组。
    # numpy.ravel(a, order='C')
    a = np.arange(8).reshape(2, 4)
    print('原数组：')
    print(a)
    print('调用 ravel 函数后：')
    print(a.ravel())
    print('F 风格顺序调用 ravel 函数之后：')
    print(a.ravel(order='F'))


# todo 2、数组转置操作

"""
数组转置操作：
函数名称    说明
transpose   将数组的维度值进行对换，比如二维数组维度 (2,4) 使用该方法后为 (4,2)。
ndarray.T   与 transpose 方法相同。
rollaxis    沿着指定的轴向后滚动至规定的位置。
swapaxes    对数组的轴进行对换。
"""


def numpy_t():
    # todo 1) numpy.transpose()

    # numpy.transpose() 用于对换多维数组的维度，比如二维数组使用此方法可以实现矩阵转置,语法格式如下：numpy.transpose(arr, axes)

    # 参数说明如下：ndarray.T 的使用方法与其类似
    # arr：要操作的数组
    # axes：可选参数，元组或者整数列表，将会按照该参数进行转置。
    a = np.arange(12).reshape(3, 4)
    print(a)

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    print(np.transpose(a))

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # todo 2) numpy.rollaxis()

    '''该方法表示沿着指定的轴，向后滚动至一个特定位置，格式如下：

        numpy.rollaxis(arr, axis, start)
    
    参数说明：
        
        arr：要传入的数组；
        axis：沿着哪条轴向后滚动，其它轴的相对位置不会改变；
        start：默认以 0 轴开始，可以根据数组维度调整它的值。
    '''

    # todo 3) numpy.swapaxes()
    '''该方法用于交换数组的两个轴，其语法格式如下：

        numpy.swapaxes(arr, axis1, axis2) 
    '''

    # 创建了三维的 ndarray
    a = np.arange(27).reshape(3, 3, 3)
    print(a)

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # 对换0轴与2轴
    print(np.swapaxes(a, 2, 0))


# todo 4、 修改数组维度操作

'''
修改数组维度的操作，主要有以下方法：

    数组维度修改
    函数名称	        描述说明
    broadcast	    生成一个模拟广播的对象。
    broadcast_to	将数组广播为新的形状。
    expand_dims	    扩展数组的形状。
    squeeze	        从数组的形状中删除一维项。
'''


def numpy_modifying_dime():
    # todo  1) numpy.broadcast()
    # 返回值是数组被广播后的对象，该函数以两个数组作为输入参数

    a = np.array([[1], [2], [3]])
    b = np.array([4, 5, 6])
    # 对b广播a
    d = np.broadcast(a, b)
    # d它拥有 iterator 属性
    r, c = d.iters
    print(next(r), next(c))

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    print(next(r), next(c))

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # 使用broadcast将a与b相加
    e = np.broadcast(a, b)
    f = np.empty(e.shape)
    f.flat = [x + y for (x, y) in e]
    print(f)

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    print(a + b)

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # todo  2) numpy.broadcast_to()

    #  该函数将数组广播到新形状中，它在原始数组的基础上返回一个只读视图。 如果新形状不符合 NumPy 的广播规则，则会抛出 ValueError 异常。
    #  函数的语法格式：numpy.broadcast_to(array, shape, subok)
    a = np.arange(4).reshape(1, 4)
    print("原数组", a)
    print('调用 broadcast_to 函数之后：')
    print(np.broadcast_to(a, (4, 4)))

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # todo 3) numpy.expand_dims()
    # 在指定位置插入新的轴，从而扩展数组的维度，语法格式:numpy.expand_dims(arr, axis)
    # 参数说明：0 横轴加了一层  1 纵轴加了一层
    # arr：输入数组
    # axis：新轴插入的位置

    x = np.array(([1, 2], [3, 4]))
    print('数组 x：')
    print(x)
    # 在 0 轴处插入新的轴
    y = np.expand_dims(x, axis=0)
    print('数组 y：')
    print(y)
    print('\n')
    print('数组 x 和 y 的形状：')
    print(x.shape, y.shape)

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # todo 4) numpy.squeeze()

    #  删除数组中维度为 1 的项，例如，一个数组的 shape 是 (5,1)，经此函数后，shape 变为 (5,) 。其函数语法格式如下：numpy.squeeze(arr, axis)
    # 参数说明：
    # arr：输入数的组；
    # axis：取值为整数或整数元组，用于指定需要删除的维度所在轴，指定的维度值必须为 1 ，否则将会报错，若为 None，则删除数组维度中所有为 1 的项。

    a = np.arange(9).reshape(1, 3, 3)
    print(a)
    b = np.squeeze(a)
    print(b)
    print('数组 a 和 b 的形状：')
    print(a.shape, b.shape)

    # todo 5、连接与分割数组操作
    """
    连接与分割数组方法
    -------------------

    连接数组方法:
    +----------------+-------------------------------------------------+
    | 函数名称       | 描述说明                                        |
    +----------------+-------------------------------------------------+
    | concatenate    | 沿指定轴连接两个或者多个相同形状的数组。        |
    | stack          | 沿着新的轴连接一系列数组。                      |
    | hstack         | 按水平顺序堆叠序列中数组（列方向）。            |
    | vstack         | 按垂直方向堆叠序列中数组（行方向）。            |
    +----------------+-------------------------------------------------+

    分割数组方法:
    +----------------+-------------------------------------------------+
    | 函数名称       | 描述说明                                        |
    +----------------+-------------------------------------------------+
    | split          | 将一个数组分割为多个子数组。                    |
    | hsplit         | 将一个数组水平分割为多个子数组（按列）。        |
    | vsplit         | 将一个数组垂直分割为多个子数组（按行）。        |
    +----------------+-------------------------------------------------+
    """


def numpy_conn_split():
    # todo 1) 连接数组操作

    '''
    numpy.concatenate() 沿指定轴连接相同形状的两个或多个数组，格式如下：

        numpy.concatenate((a1, a2, ...), axis)

    参数说明：

    a1, a2, ...：表示一系列相同类型的数组；
    axis：沿着该参数指定的轴连接数组，默认为 0。

    实例说明：创建两个 a 、b 数组，并沿指定轴将它们连接起来。注意两个数组的形状要保持一致。
    '''

    # 创建数组a
    a = np.array([[10, 20], [30, 40]])
    print('创建数组a', a)

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # 创建数组b
    b = np.array([[50, 60], [70, 80]])
    print('创建数组b', b)

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # 沿轴 0 连接两个数组
    print(np.concatenate((a, b)))

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # 沿轴 1 连接两个数组
    print(np.concatenate((a, b), axis=1))

    # 数组连接操作至少需要两个维度相同的数组，才允许对它们进行垂直或者水平方向上的操作。
    a = np.array([[1, 2], [3, 4]])
    b = np.array([[5, 6], [7, 8]])
    # 垂直堆叠
    c = np.vstack((a, b))
    print(c)

    # 2) 分割数组操作
    '''
    numpy.split() 沿指定的轴将数组分割为多个子数组，语法格式如下：

    numpy.split(ary, indices_or_sections, axis)
    
    参数说明：
    
    ary：被分割的数组
    indices_or_sections：若是一个整数，代表用该整数平均切分，若是一个数组，则代表沿轴切分的位置（左开右闭）；
    axis：默认为0，表示横向切分；为1时表示纵向切分。
    '''

    a = np.arange(6)
    # 原数组
    print(a)
    # 将数组分为二个形状大小相等的子数组
    b = np.split(a, 2)
    print(b)
    # 将数组在一维数组中标明要位置分割
    b = np.split(a, [3, 4])
    print(b)

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # hsplit() 的使用方法

    # arr1数组
    arr1 = np.floor(10 * np.random.random((2, 6)))
    print(arr1)
    # 拆分后数组
    print(np.hsplit(arr1, 3))


def main():
    # numpy_variable_dimension()
    # numpy_t()
    # numpy_modifying_dime()
    numpy_conn_split()


if __name__ == '__main__':
    main()
